// ES6 Class 语法糖预备知识点，Class 用于简化和美化构造函数
// 类的方法内部如果含有 this，它默认指向类的实例
// constructor() 方法是类的默认方法，通过 new 命令生成对象实例时，自动调用该方法
// static 关键字，就表示该方法不会被实例继承，而是直接通过类来调用，即为静态方法
// 实例属性除了定义在 constructor() 方法里面的 this 上面，也可以定义在类的最顶层

// => 使用说明
// 1. 初始化连接：const ws = SingletonWebsocket.getInstance(port);
// 2. 手动关闭连接：先初始化，再利用其调用 manualCloseWS()，此处为 ws.manualCloseWS()

/**
 * @description 单例 websocket 类的基础封装
 * @param socket websocket 实例
 * @param isWebsocketConnect websocket 连接状态
 * @param heartbeatPulseInterval 心跳包定时器
 * @param reconnectWebsocketInterval 重连定时器
 * @param reconnectWebsocketTime 重连次数
 */
class SingletonWebsocket {
  // * class 实例属性演示（方案一）：_count = 0; 建议以下划线为前缀

  socket = null;
  isWebsocketConnect = false;
  heartbeatPulseInterval = null;
  reconnectWebsocketInterval = null;
  reconnectWebsocketTime = 30;

  constructor(port) {
    // * class 实例属性演示（方案二）：this._count = 0; 推荐方案一

    // 根据条件判断是否初始化当前 websocket 连接
    if (!this.isWebsocketConnect) {
      this.init(port);
    }
  }

  static instance;
  static getInstance(port) {
    if (!this.instance) {
      this.instance = new SingletonWebsocket(port);
    }
    return this.instance;
  }

  init(port) {
    // websocket 各项生命周期事件处理逻辑
    // 回调函数建议放在 init 函数外面，美化代码同时方便后续更新维护

    // 初始化连接 WS 地址
    this.socket = new WebSocket(port);

    this.socket.addEventListener("open", () => {
      console.log("class 中的 this", this);
      if (~~this.socket.readyState === 1) {
        this.isWebsocketConnect = true;
        this.handleWebsocketConnect();
        console.log("WS 连接成功：", this.socket);
      }
    });

    // * 从 websocket 接收到的响应中解构出 JSON 数据并解析成 对象格式
    this.socket.addEventListener("message", ({ data }) => {
      const wsData = JSON.parse(data);
      console.log("WS 接收消息，数据为：", wsData);
    });

    this.socket.addEventListener("close", () => {
      this.isWebsocketConnect = false;
      this.heartbeatPulseInterval = null;
      clearInterval(this.heartbeatPulseInterval);
      console.log("WS 关闭：", this.socket);
    });

    this.socket.addEventListener("error", (event) => {
      this.isWebsocketConnect = false;
      this.reconnectWebsocketInterval = null;
      this.heartbeatPulseInterval = null;
      clearInterval(this.reconnectWebsocketInterval);
      clearInterval(this.heartbeatPulseInterval);
      console.log("WS 连接错误：", this.socket, event);
    });
  }

  // 绑定 websocket 成功时的回调处理
  handleWebsocketConnect() {
    this.heartbeatPulse();
  }

  // 重连当前 websocket，建议重连一定次数就关闭连接，暂为 30 次
  reconnectWebsocket() {
    if (!this.isWebsocketConnect) {
      this.reconnectWebsocketInterval = setInterval(() => {
        if (this.reconnectWebsocketTime > 0) {
          this.reconnectWebsocketTime--;
          this.init();
        } else {
          this.reconnectWebsocketInterval = null;
          clearInterval(this.reconnectWebsocketInterval);
        }
      }, 20 * 1000);
    }
  }

  // 心跳包
  heartbeatPulse() {
    // * 只能有一个定时器，所以此处才使用具名定时器，其值为数字类型
    if (this.isWebsocketConnect && !this.heartbeatPulseInterval) {
      this.heartbeatPulseInterval = setInterval(() => {
        this.socket.send(JSON.stringify({ msg: "保持联系中..." }));
      }, 60 * 1000);
    }
  }

  // 手动关闭当前 websocket 连接
  manualCloseWS() {
    this.socket.close();
  }
}
